package parser

import (
	"gum/interpreter/ast"
	"gum/interpreter/lexer"
	"gum/interpreter/token"
)

type PrefixFn func() ast.Expression
type InfixFn func(ast.Expression) ast.Expression

var (
	LOWEST        = 0
	precedenceMap = map[int]int{
		token.Plus:         2,
		token.PlusEqual:    2,
		token.Minus:        2,
		token.MinusEqual:   2,
		token.Product:      3,
		token.ProductEqual: 3,
		token.Divide:       3,
		token.DivideEqual:  3,
		token.Lparen:       5,
	}
)

type Parser struct {
	Lexer *lexer.Lexer
	Cur   *token.Token
	Next  *token.Token

	PrefixFns map[int]PrefixFn
	InfixFns  map[int]InfixFn
}

func (p *Parser) NextToken() {
	p.Cur = p.Next
	p.Next = p.Lexer.ReadToken()
}

func NewParser(input string) *Parser {
	l := lexer.NewLexer(input)
	p := Parser{
		Lexer:     l,
		Cur:       nil,
		Next:      nil,
		PrefixFns: map[int]PrefixFn{},
		InfixFns:  map[int]InfixFn{},
	}
	p.registerPrefixFn(token.Identifier, p.parseSymbol)
	p.registerPrefixFn(token.Digits, p.parseDigit)
	p.registerPrefixFn(token.Bang, p.parsePrefixExpression)
	p.registerPrefixFn(token.Product, p.parsePrefixExpression)
	p.registerPrefixFn(token.Lbracket, p.parsePrefixExpression) // 列表
	p.registerPrefixFn(token.Lparen, p.parseGroupExpression)
	p.registerInfixFn(token.Plus, p.parseInfixExpression)
	p.registerInfixFn(token.PlusEqual, p.parseInfixExpression)
	p.registerInfixFn(token.Minus, p.parseInfixExpression)
	p.registerInfixFn(token.MinusEqual, p.parseInfixExpression)
	p.registerInfixFn(token.Product, p.parseInfixExpression)
	p.registerInfixFn(token.ProductEqual, p.parseInfixExpression)
	p.registerInfixFn(token.Divide, p.parseInfixExpression)
	p.registerInfixFn(token.DivideEqual, p.parseInfixExpression)
	p.NextToken()
	p.NextToken()
	return &p
}

func (p *Parser) Parse() ast.Program {
	program := ast.Program{}
	for {
		if !p.Lexer.EOF() {
			statement := p.parseStatement()
			if statement != nil {
				program.Statements = append(program.Statements, statement)
			}
		} else {
			break
		}
	}
	return program
}

func (p *Parser) parseStatement() ast.Statement {
	switch p.Cur.Type {
	case token.Let:
		return p.parseLetStatement()
	case token.Return:
		return p.parseReturnStatement()
	default:
		return p.parseExpressionStatement()
	}
}

func (p *Parser) parseLetStatement() ast.Statement {
	statement := ast.LetStatement{
		Symbol: nil,
		Value:  nil,
	}
	p.NextToken()
	statement.Symbol = p.parseSymbol()
	p.NextToken() // skip symbol
	p.NextToken() // skip =
	statement.Value = p.parseExpression(LOWEST)
	p.NextToken() // skip ;
	return &statement
}

func (p *Parser) parseSymbol() ast.Expression {
	return &ast.Identifier{
		Name:  p.Cur.Literal,
		Token: p.Cur,
	}
}

func (p *Parser) parseDigit() ast.Expression {
	return &ast.Digits{
		Name:  p.Cur.Literal,
		Token: p.Cur,
	}
}

func (p *Parser) parseReturnStatement() ast.Statement {
	statement := ast.ReturnStatement{Value: nil}
	p.NextToken() // skip return
	statement.Value = p.parseExpression(LOWEST)
	p.NextToken() // skip ;
	return &statement
}

func (p *Parser) parseExpressionStatement() ast.Statement {
	statement := ast.ExpressionStatement{Value: nil}
	statement.Value = p.parseExpression(LOWEST)
	return &statement
}

func (p *Parser) parseExpression(precedence int) ast.Expression {
	prefix := p.PrefixFns[p.Cur.Type]
	if prefix == nil {
		return nil
	}
	exp := prefix()
	// 不能现在跳过字符，应该交由下一步来判断是否跳过，判断下一个字符的优先级和当前的优先级
	for {
		if p.Next.Type != token.SemiColon && precedence < p.lookupPrecedence(p.Next.Type) {
			infix := p.InfixFns[p.Next.Type]
			if infix != nil {
				exp = infix(exp)
			} else {
				break
			}
		} else {
			break
		}
	}
	if p.Next.Type == token.SemiColon {
		p.NextToken()
	}
	return exp
}

func (p *Parser) registerPrefixFn(identifier int, symbol PrefixFn) {
	p.PrefixFns[identifier] = symbol
}

func (p *Parser) registerInfixFn(identifier int, symbol InfixFn) {
	p.InfixFns[identifier] = symbol
}

func (p *Parser) parseInfixExpression(expression ast.Expression) ast.Expression {
	p.NextToken()
	statement := ast.InfixExpression{
		Operator: p.Cur.Literal,
		Left:     expression,
		Right:    nil,
	}
	precedence := p.lookupPrecedence(p.Cur.Type)
	p.NextToken() // skip operator
	statement.Right = p.parseExpression(precedence)
	//p.NextToken() // skip ; BUG 不能跳过，不确定是否会跳过正常字符
	return &statement
}

func (p *Parser) parsePrefixExpression() ast.Expression {
	return nil
}

func (p *Parser) lookupPrecedence(t int) int {
	pre, ok := precedenceMap[t]
	if ok {
		return pre
	}
	return LOWEST
}

func (p *Parser) parseGroupExpression() ast.Expression {
	p.NextToken()
	statement := ast.GroupExpression{Value: nil}
	statement.Value = p.parseExpression(LOWEST)
	if p.Next.Type == token.Rparen {
		p.NextToken()
	}
	return &statement
}
